﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Caching;
using System.Data.SqlClient;

namespace WX_Service
{
    public class MemoryCacheHelper
    {
        private static object _locker = new object();
        private static MemoryCacheHelper instance;
        //委托移除以后  
        private CacheEntryRemovedCallback _RemovedCallback;
        //委托即将移除时  
        private CacheEntryUpdateCallback _UpdateCallback;

        private MemoryCacheHelper()
        {

        }

        public static MemoryCacheHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (_locker)
                    {
                        if (instance == null)
                        {
                            instance = new MemoryCacheHelper();
                        }
                    }
                }
                return instance;
            }
        }

        /// <summary>  
        /// 获取缓存中的值  
        /// </summary>  
        /// <typeparam name="T">类型</typeparam>  
        /// <param name="key">标识的Key(保持唯一)</param>  
        /// <param name="value">需要缓存的缓存项的值</param>  
        /// <param name="slidingExpiration">该值指示如果某个缓存项在给定时段内未被访问，是否应被逐出</param>  
        /// <param name="absoluteExpiration">该值指示是否应在指定持续时间过后逐出某个缓存项</param>  
        /// <returns></returns>  
        public T GetCacheItem<T>(string key, T value, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
        {
            return GetCacheItem<T>(key, value, null, slidingExpiration, absoluteExpiration);
        }

        /// <summary>  
        /// 获取缓存中的值  
        /// </summary>  
        /// <typeparam name="T">类型</typeparam>  
        /// <param name="key">标识的Key(保持唯一)</param>  
        /// <param name="value">需要缓存的缓存项的值</param>  
        /// <param name="Command">SQL执行的Command</param>  
        /// <param name="slidingExpiration">该值指示如果某个缓存项在给定时段内未被访问，是否应被逐出</param>  
        /// <param name="absoluteExpiration">该值指示是否应在指定持续时间过后逐出某个缓存项</param>  
        /// <returns></returns>  
        public T GetCacheItem<T>(string key, T value, SqlCommand Command = null, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("Invalid cache key");
            if (value == null)
                throw new ArgumentNullException("value");
            if (slidingExpiration == null && absoluteExpiration == null)
                throw new ArgumentException("Either a sliding expiration or absolute must be provided");
            ObjectCache cachelist = MemoryCache.Default;
            if (cachelist[key] == null)
            {
                lock (_locker)
                {
                    var item = new CacheItem(key, value);
                    var policy = CreatePolicy(_RemovedCallback, _UpdateCallback, Command, slidingExpiration, absoluteExpiration);
                    MemoryCache.Default.Add(item, policy);
                }
            }
            return (T)Convert.ChangeType(cachelist[key], typeof(T));
        }

        /// <summary>  
        /// 获取缓存中的值  
        /// </summary>  
        /// <typeparam name="T">类型</typeparam>  
        /// <param name="key">标识的Key(保持唯一)</param>  
        public T GetCacheItem<T>(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("Invalid cache key");
            ObjectCache cachelist = MemoryCache.Default;
            return (T)Convert.ChangeType(cachelist[key], typeof(T));
        }

        /// <summary>  
        /// 创建指定缓存项的一组逐出和过期详细信息  
        /// </summary>  
        /// <param name="RemovedCallback">在从缓存中移除某个项后将调用该委托</param>  
        /// <param name="UpdateCallback">在从缓存中移除某个缓存项之前将调用该委托</param>  
        /// <param name="slidingExpiration">该值指示如果某个缓存项在给定时段内未被访问，是否应被逐出</param>  
        /// <param name="absoluteExpiration">该值指示是否应在指定持续时间过后逐出某个缓存项</param>  
        /// <returns></returns>  
        private CacheItemPolicy CreatePolicy(CacheEntryRemovedCallback RemovedCallback, CacheEntryUpdateCallback UpdateCallback, SqlCommand Command, TimeSpan? slidingExpiration, DateTime? absoluteExpiration)
        {
            var policy = new CacheItemPolicy();
            if (absoluteExpiration.HasValue)
                policy.AbsoluteExpiration = absoluteExpiration.Value;
            else if (slidingExpiration.HasValue)
                policy.SlidingExpiration = slidingExpiration.Value;
            policy.Priority = CacheItemPriority.Default;
            if (RemovedCallback != null)
                policy.RemovedCallback = RemovedCallback;
            if (UpdateCallback != null)
                policy.UpdateCallback = UpdateCallback;
            if (Command != null)
            {
                System.Data.SqlClient.SqlDependency mSqlDependency = new System.Data.SqlClient.SqlDependency(Command);
                policy.ChangeMonitors.Add(new SqlChangeMonitor(mSqlDependency));
            }
            return policy;
        }

        /// <summary>  
        /// 事件定义在从缓存中移除某个缓存项后触发  
        /// </summary>  
        public event CacheEntryRemovedCallback RemovedCallback
        {
            add
            {
                lock (_locker)
                {
                    _RemovedCallback += value;
                }
            }
            remove
            {
                lock (_locker)
                {
                    _RemovedCallback -= value;
                }
            }
        }
        /// <summary>  
        /// 事件定义在从缓存中即将移除某个缓存项时触发  
        /// </summary>  
        public event CacheEntryUpdateCallback UpdateCallback
        {
            add
            {
                lock (_locker)
                {
                    _UpdateCallback += value;
                }
            }
            remove
            {
                lock (_locker)
                {
                    _UpdateCallback -= value;
                }
            }
        }

        /// <summary>  
        /// 定义在从缓存中移除某个缓存项后触发  
        /// </summary>  
        /// <param name="args">有关已从缓存中移除的缓存项的信息</param>  
        protected virtual void OnRemovedCallback(CacheEntryRemovedArguments args)
        {
            if (_RemovedCallback != null)
                _RemovedCallback(args);
        }
        /// <summary>  
        /// 定义在从缓存中即将移除某个缓存项时触发  
        /// </summary>  
        /// <param name="args">有关将从缓存中移除的缓存项的信息</param>  
        protected virtual void OnUpdateCallback(CacheEntryUpdateArguments args)
        {
            if (_UpdateCallback != null)
                _UpdateCallback(args);
        }  
    }
}